/**
 * Vector3.java
 * 
 * @date: 2012-02-28
 * @author: Xiaoyu Guo (MUN# 200982155)
 * This file is part of the course project for COMP 6752, 
 * Application of Computer Graphics. All the file is written
 * by myself.
 */
package mun.cs6752.project.model;

/** Defines a 3-tuple vector.
 * Vector3 can represent a 3D point in Cartesian coordinate, or a 2D point in 
 * Homogeneous coordinate. 
 * Please note that there are only 3 values in Vector3, and Z and W are actually
 * the same element.
 * @author Xiaoyu Guo
 */
public class Vector3 extends Vector {
    private static final int SIZE = 3;
    private static final int INDEX_X = 0;
    private static final int INDEX_Y = 1;
    private static final int INDEX_Z = 2;

    /**
     * @param source
     */
    public Vector3(double[] source) {
        super(source);
        
        if(size != SIZE){
            throw new IllegalArgumentException("size must be 3");
        }
    }
    
    public Vector3(){
        super(SIZE);
    }
    
    public Vector3(double x, double y, double z){
        super(SIZE);
        setX(x);
        setY(y);
        setZ(z);
    }
    
    public Vector3(Vector value){
        super(SIZE);

        checkNull(value);
        if(value.size < SIZE){
            throw new IllegalArgumentException("size must be 3");
        }
        
        setX(value.get(INDEX_X));
        setY(value.get(INDEX_Y));
        setZ(value.get(INDEX_Z));
    }
    
    public double getX(){
        return get(INDEX_X);
    }
    public void setX(double x){
        set(INDEX_X, x);
    }
    
    public double getY(){
        return get(INDEX_Y);
    }
    
    public void setY(double y){
        set(INDEX_Y, y);
    }
    
    public double getZ(){
        return get(INDEX_Z);
    }
    
    public void setZ(double z){
        set(INDEX_Z, z);
    }
    
    public double getW(){
        return get(INDEX_Z);
    }
    
    public void setW(double w){
        set(INDEX_Z, w);
    }
    
    /* (non-Javadoc)
     * @see mun.cs6752.project.model.Vector#getLength()
     */
    @Override
    public double getLength(){
        return Math.sqrt(getX() * getX() + getY() * getY() + getZ() * getZ());
    }
    
    /* (non-Javadoc)
     * @see mun.cs6752.project.model.Vector#getUnitVector()
     */
    @Override
    public Vector3 getUnitVector(){
        double length = getLength();
        if(length == 0){
            return this;
        }
        return new Vector3(getX() / length, getY() / length, getZ() / length);
    }
    
    public Vector3 add(Vector3 value){
        checkNull(value);
        return new Vector3(
            getX() + value.getX(),
            getY() + value.getY(),
            getZ() + value.getZ()
        );
    }
    
    public Vector3 minus(Vector3 value){
        checkNull(value);
        return new Vector3(
            getX() - value.getX(),
            getY() - value.getY(),
            getZ() - value.getZ()
        );
    }
    
    @Override
    public Vector3 negate(){
        return new Vector3(
            -getX(), -getY(), -getZ()
        );
    }
    
    /** Formalize the vector.
     * @return a formalized homogeneous vector
     */
    public Vector3 formalize(){
        if(getZ() == 0){
            throw new IllegalArgumentException("w equals to 0");
        }
        return new Vector3(
            getX() / getZ(),
            getY() / getZ(),
            1
        );
    }
    
    /**
     * Calculate the cross product
     * @param value
     * @return
     */
    public Vector3 crossProduct(Vector3 value){
        checkNull(value);
        Vector3 result = new Vector3();
        
        result.setX(getY() * value.getZ() - getZ() * value.getY());
        result.setY(getZ() * value.getX() - getX() * value.getZ());
        result.setZ(getX() * value.getY() - getY() * value.getX());
        
        return result;
    }
    
    /** Calculate the dot product
     * @param value
     * @return
     */
    public double dotProduct(Vector3 value){
        checkNull(value);
        return getX() * value.getX() + getY() * value.getY() + getZ() * value.getZ();
    }
    
    /** Gets the homogeneous coordinate
     * @return (x, y, z, 1)
     */
    public Vector4 getHomogeneous(){
        return new Vector4(getX(), getY(), getZ(), 1);
    }
    
    /** Converts to Cartesian coordinate
     * @return (x / w, y / w)
     */
    public Vector2 getCartesian(){
        if(getZ() == 0){
            throw new IllegalArgumentException("z equals to 0");
        }
        return new Vector2(getX() / getZ(), getY() / getZ());
    }
}
